package com.lagou.dubbo.common;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

public class MonitorHelper {

    // 测试时间（1分钟）
    public static final long TEST_TIME = 60000;
    // 间隔时间（控制请求频率，如果cpu压力大，可以适当增加该值）
    public static final long PAUSE_TIME = 0;
    // 测试状态
    public static boolean TESTING = false;

    // 保存tp信息用（使用ConcurrentHashMap和AtomicLong解决并发场合map添加数据的线程安全问题）
    public static Map<String, Map<Long, AtomicLong>> tpMap = new ConcurrentHashMap<>();
    // 打印tp信息用
    public static Map<String, Map<Long, AtomicLong>> printTPMap = new ConcurrentHashMap<>();

    private static ExecutorService executorService = Executors.newFixedThreadPool(3);

    /**
     * 并行执行调用请求
     *
     * @param runnable
     */
    public static void execute(Runnable runnable) {
        executorService.execute(runnable);
    }

    /**
     * 统计tp信息
     *
     * @param methodName
     * @param executeTime
     */
    public static void countTP(String methodName, Long executeTime) {

        // 使用两层map结构保存每个方法的tp信息
        // 传统方式
        if (MonitorHelper.tpMap.containsKey(methodName)) {
            Map methodMap = MonitorHelper.tpMap.get(methodName);

            if (methodMap.containsKey(executeTime)) {
                ((AtomicLong) methodMap.get(executeTime)).incrementAndGet();

            } else {
                methodMap.put(executeTime, new AtomicLong(1));
            }
        } else {
            Map<Long, AtomicLong> methodMap = new ConcurrentHashMap();
            methodMap.put(executeTime, new AtomicLong(1));
            MonitorHelper.tpMap.put(methodName, methodMap);
        }

        // lambda方式（可读性和开发效率不高，不容易控制程序流程）
//        MonitorHelper.tpMap.computeIfAbsent(methodName, key -> new ConcurrentHashMap<>() {{
//            put(executeTime, new AtomicLong(0)); // 初始需要设置为0，然后会执行MonitorHelper.tpMap.get(methodName).computeIfPresent()
//        }});
//        MonitorHelper.tpMap.get(methodName).computeIfPresent(executeTime, (key, value) -> {
//            value.incrementAndGet();
//            return value;
//        });
//        MonitorHelper.tpMap.get(methodName).computeIfAbsent(executeTime, key -> new AtomicLong(1));
    }

    /**
     * 打印tp信息
     */
    public static void printTP() {

        // 复制一个用于打印的tp信息快照，避免打印过程中，统计信息实时的变化
        copyTPMap();

        System.out.println();
        System.out.println("TP监控数据 " + CommonUtils.now());
        System.out.println("【耗时时间升序】耗时时间=调用次数");
        System.out.println(printTPMap);

        // 计算每个接口的tp结果
        for (Map.Entry<String, Map<Long, AtomicLong>> method : printTPMap.entrySet()) {

            // 将统计信息进行升序排序，map从（key:响应时间, value:调用次数）改为（key:统计数值, value:响应时间）
            TreeMap<Long, Long> sortMap = new TreeMap<>(Comparator.naturalOrder());
            AtomicLong idx = new AtomicLong(0L);

            method.getValue().forEach((key, value) -> {
                sortMap.put(idx.get(), key);
                idx.updateAndGet(v -> v + value.get());
            });

            // 计算tp90、tp99（使用已经排过序的tp信息，根据调用次数占比，取得对应的响应时间）
            Long total = method.getValue().values().stream().mapToLong(o -> o.get()).sum();
            Double idx90 = Math.ceil((double) total * 90 / 100);
            Double idx99 = Math.ceil((double) total * 99 / 100);
            Map.Entry<Long, Long> idx90Entry = sortMap.floorEntry(idx90.longValue());
            Map.Entry<Long, Long> idx99Entry = sortMap.floorEntry(idx99.longValue());

            Long TP90 = idx90Entry.getValue();
            Long TP99 = idx99Entry.getValue();

            // 显示tp计算结果
            System.out.println(method.getKey() + " TP90:" + TP90 + ", TP99:" + TP99);
//            System.out.println(sortMap);
//            System.out.println();
        }
    }

    /**
     * 复制一个用于打印的tp信息快照
     */
    public static synchronized void copyTPMap() {
        printTPMap.putAll(tpMap);
    }

    /**
     * 清除tp信息
     */
    public static void cleanTP() {
        tpMap = new ConcurrentHashMap<>();

        System.out.println();
        System.out.println("已清除TP监控数据");
        System.out.println();
    }
}
